/**
  ETFDAQ Project
  \class TAv895
  \brief 16 ch ECL input LED VME module from CAEN Corp. of Italy
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2024-09-03
  \date 2024-09-03 last modified

  \copyright Copyright (c) 2024 IMP-CAS with LGPLv3 LICENSE
*/

#include <iostream>
#include <bitset>
#include "TAv895.h"

using std::cout;
using std::endl;

TAv895::TAv895(TAController *ctrl, const TAYaml &c) : TACAENCard(ctrl, c),
fModuleType(0), fManufacturerNo(0), fWidth{0}{
  fCardPurpose = ECardPurpose::LED;
  fNCh = 16;
} // end ctor

TAv895::~TAv895(){}

void TAv895::Initialize(){
  Config(); // config the card according to user yaml input
  Identify(); // read card rom for manufacturer info

  // set output width //
  const auto &wc = fConfig["width"];
  if(wc){
    SetOutputWidth(0, fWidth[0] = wc[0].as<int>());
    SetOutputWidth(1, fWidth[1] = wc[1].as<int>());
  } // end if
  else{
    SetOutputWidth(0, 0); // default to 5ns
    SetOutputWidth(1, 0); // default to 5ns
  } // end else

  if(fConfig["majority"])
    RegWriteA32D16(v895reg::MajorityThre, fConfig["majority"].as<int>() & 0xffff);

  // set thresholds //
  if(fThres.size() == fNCh){
    for(size_t i = 0; i < fNCh; i++) SetThreshold(i, fThres[i]);
  } else{
    SetThreshold(-1, 0); // default to 0
    fThres.resize(fNCh);
    fThres.assign(fThres.size(), true);
  } // end else

  // enable or disable channels
  if(fChOn.size() == fNCh){
    std::bitset<64> v;
    for(size_t i = 0; i < fNCh; i++) v.set(i, fChOn[i]);
    SetChOnPattern(v.to_ulong() & 0xffff);
  } else{
    SetChOnPattern(0xffff); // all on
    fChOn.resize(fNCh);
    fChOn.assign(fChOn.size(), true);
  } // end else
} // end member function Initialize

// read card info (e.g. serialNo.)
void TAv895::Identify(){
  const int sn = RegReadA32D16(v895reg::SerialNo);
  fSerialNo = sn & 0xfff;
  fVersion = sn>>11 & 0xf;

  const int tp = RegReadA32D16(v895reg::ModuleType);
  fModuleType = tp & 0x3ff;
  fManufacturerNo = tp>>9 & 0x3f;
} // end member function Identify

void TAv895::Print() const{
  printf("\n\033[36;1m------ slot %d: %s ------\033[0m\n", fSlot, fCardName.data());
  printf("baseaddr: 0x%08x\n", fBaseAddress);
  printf("SerialNo: %d, Version: %d\nManuf. No.: %d, Module Type: %d\n",
    fSerialNo, fVersion, fManufacturerNo, fModuleType);
  cout << "IRQ: NOT SUPPORTED" << endl;

  // print thresholds and width
  cout << "___ Thresholds and width for all " << fNCh << " channels ___" << endl;
  cout << "ch\t state\t thre\t width" << endl;
  for(size_t i = 0; i < fNCh; i++){
    printf("%02lu\t %s\t %u \t %u\n", i,
      fChOn[i] ? "on" : "off", fThres[i], i < 8 ? fWidth[0] : fWidth[1]);
  } // end for over i
} // end member function Print

// 16 bits corresponds to ch 0-15 respectively. 1: enable; 0:disable
void TAv895::SetChOnPattern(uint16_t pattern) const{
  RegWriteA32D16(v895reg::PatternInhibit, pattern);
} // end member function SetChOn

// ch starts from 0, -1 means all channels
// only the first 8 bits in thre are valid by default
void TAv895::SetThreshold(int ch, uint16_t thre) const{
  if(ch < 0) for(int i = fNCh; i--;) SetThreshold(i, thre);
  else RegWriteA32D16(v895reg::Thresholds + ch * fThreAddrIncre, thre & 0xff);
} // end member function SetThreshold

// first: 0-7; false: 8-15; w: 0: 5ns; 255: 40ns
void TAv895::SetOutputWidth(bool first, int w) const{
  if(first) RegWriteA32D16(v895reg::Width0_7, w & 0xff);
  else RegWriteA32D16(v895reg::Width8_15, w & 0xff);
} // end member function SetOutputWidth
